/**
 * 
 */
package cn.springsoter.core.security.config;

import java.security.KeyPair;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.crypto.SecretKey;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.springsoter.core.security.constant.SecurityConstant;
import cn.springsoter.core.security.props.SecurityProperties;
import cn.springsoter.core.tool.exception.SecureException;
import cn.springsoter.core.tool.utils.FileUtil;
import cn.springsoter.core.tool.utils.KeyStoreUtil;
import cn.springsoter.core.tool.utils.RSAUtil;
import cn.springsoter.core.tool.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @author kevin
 *
 */
@Slf4j
@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnProperty(name = "springsoter.security.enable", havingValue = "true")
public class SecurityConfig {
	
	private final SecurityProperties securityProperties;
	
	@Autowired
	public SecurityConfig(SecurityProperties securityProperties) {
		this.securityProperties = securityProperties;
	}
	
	@Bean
	public SecurityProperties securityProperties() {
		return securityProperties;
	}
	
	/**
	 * 服务启动执行：生成RSA和AES的key到指定的目录下
	 */
	@PostConstruct
	public void storeRSAAndAESKey() {
		if(StringUtils.isBlank(securityProperties.getPassword()) 
				|| StringUtils.isBlank(securityProperties.getAppName())
				|| StringUtils.isBlank(securityProperties.getKeystorePath())) {
			throw new SecureException("the properties: springsoter.security.password or springsoter.security.appName or springsoter.security.keystorePath is empty!");
		}
		
		if(StringUtil.isSpecialChar(securityProperties.getAppName())) {
			throw new SecureException("the properties: springsoter.security.appName value is invalid!");
		}
		
		Map<String, String> map = RSAUtil.generateRSAKeyPair();
    	//RSA
    	String publicKey = map.get(RSAUtil.RSA_PUBLIC_CACHE_KEY);
    	String privateKey = map.get(RSAUtil.RSA_PRIVATE_CACHE_KEY);
    	
    	//获取一个32bytes的AES明文密钥key，密钥长度最大支持32个字节
    	String aesKey = StringUtil.getRandomString(32);
    	
    	//用RSA的公钥加密AES Key
    	String encryptAesKey = RSAUtil.encryptByPublicKey(aesKey, RSAUtil.getPublicKeyFromString(publicKey), CharsetUtil.UTF_8);
    	
    	String aesAlias = String.format(SecurityConstant.AES_ALIAS, securityProperties.getAppName());
    	String rsaPrivateAlias = String.format(SecurityConstant.RSA_PRIVATE_ALIAS, securityProperties.getAppName());
    	String rsaPublicAlias = String.format(SecurityConstant.RSA_PUBLIC_ALIAS, securityProperties.getAppName());
    	
    	String aesPath = String.format(SecurityConstant.KEYSTORE_PATH, securityProperties.getKeystorePath(), aesAlias);
    	String privateKeyPath = String.format(SecurityConstant.KEYSTORE_PATH, securityProperties.getKeystorePath(), rsaPrivateAlias);
    	String publicKeyPath = String.format(SecurityConstant.KEYSTORE_PATH, securityProperties.getKeystorePath(), rsaPublicAlias);
    	
        try {
        	if(!FileUtil.exist(aesPath)) {
        		KeyStoreUtil.storeKey(aesAlias, encryptAesKey, aesPath, securityProperties.getPassword());
        		log.warn("======自动生成AES keystore，位置：{}=======", aesPath);
        	}
        	if(!FileUtil.exist(privateKeyPath)) {
        		KeyStoreUtil.storeKey(rsaPrivateAlias, privateKey, privateKeyPath, securityProperties.getPassword());
        		log.warn("======自动生成AES privateKey keystore，位置：{}=======", privateKeyPath);
        	}
        	if(!FileUtil.exist(publicKeyPath)) {
        		KeyStoreUtil.storeKey(rsaPublicAlias, publicKey, publicKeyPath, securityProperties.getPassword());
        		log.warn("======自动生成AES publicKey keystore，位置：{}=======", publicKeyPath);
        	}
        	log.info("======storeRSAAndAESKey初始化完成=====");
		} catch (Exception e) {
			log.error("====StoreRSAAndAESKeyException!====", e);
		}
	}
	
	/**
	 * 服务启动执行：生成SM2和SM4的key到指定的目录下
	 */
	@PostConstruct
	public void storeSMKey() {
		if(StringUtils.isBlank(securityProperties.getPassword()) 
				|| StringUtils.isBlank(securityProperties.getAppName())
				|| StringUtils.isBlank(securityProperties.getKeystorePath())) {
			throw new SecureException("the properties: springsoter.security.password or springsoter.security.appName or springsoter.security.keystorePath is empty!");
		}
		
		if(StringUtil.isSpecialChar(securityProperties.getAppName())) {
			throw new SecureException("the properties: springsoter.security.appName value is invalid!");
		}
		//SM2
		KeyPair pair = SecureUtil.generateKeyPair("SM2");
    	byte[] privateKeyBin = pair.getPrivate().getEncoded();
    	byte[] publicKeyBin = pair.getPublic().getEncoded();
    	String privateKey = Base64.encode(privateKeyBin);
    	String publicKey = Base64.encode(publicKeyBin);
		
		//SM4
    	SymmetricCrypto sm4 = SmUtil.sm4();
    	SecretKey key = sm4.getSecretKey();
    	//明文key
    	String sm4KeyBase64 = Base64.encode(key.getEncoded());
		
    	SM2 sm2 = SmUtil.sm2(privateKeyBin, publicKeyBin);
    	//用SM2公钥加密SM4的key，私钥解密
    	String encryptSm4Key = sm2.encryptBase64(sm4KeyBase64, CharsetUtil.CHARSET_UTF_8, KeyType.PublicKey);
    	
		String sm4Alias = String.format(SecurityConstant.SM4_ALIAS, securityProperties.getAppName());
		String sm2PrivateAlias = String.format(SecurityConstant.SM2_PRIVATE_ALIAS, securityProperties.getAppName());
		String sm2PublicAlias = String.format(SecurityConstant.SM2_PUBLIC_ALIAS, securityProperties.getAppName());
		
		String sm4Path = String.format(SecurityConstant.KEYSTORE_PATH, securityProperties.getKeystorePath(), sm4Alias);
		String privateKeyPath = String.format(SecurityConstant.KEYSTORE_PATH, securityProperties.getKeystorePath(), sm2PrivateAlias);
		String publicKeyPath = String.format(SecurityConstant.KEYSTORE_PATH, securityProperties.getKeystorePath(), sm2PublicAlias);
		
		try {
			if(!FileUtil.exist(sm4Path)) {
				KeyStoreUtil.storeKey(sm4Alias, encryptSm4Key, sm4Path, securityProperties.getPassword());
				log.warn("======自动生成SM4 keystore，位置：{}=======", sm4Path);
			}
			if(!FileUtil.exist(privateKeyPath)) {
				KeyStoreUtil.storeKey(sm2PrivateAlias, privateKey, privateKeyPath, securityProperties.getPassword());
				log.warn("======自动生成SM4 privateKey keystore，位置：{}=======", privateKeyPath);
			}
			if(!FileUtil.exist(publicKeyPath)) {
				KeyStoreUtil.storeKey(sm2PublicAlias, publicKey, publicKeyPath, securityProperties.getPassword());
				log.warn("======自动生成SM4 publicKey keystore，位置：{}=======", publicKeyPath);
			}
			log.info("======storeSMKey初始化完成=====");
		} catch (Exception e) {
			log.error("====StoreSMKeyException!====", e);
		}
	}
}
